#ifndef PGHAND_H
#define PGHAND_H

#include "hand.h"

class JokerCardMgr;
class PGHandRanks;

class PGHand : public Hand
{
public:
	PGHand(CardMgr* Mgr):
		Hand(Mgr) { };

	virtual int32 numCardsToDeal() = 0;
	virtual bool8 isSplitValid(int32 twocard[2]) = 0;

	CardMgr* getMgr() { return mgr; };

	static PGHand* createHand(uint8 machid);
};


class PGTilesHand : public PGHand
{
public:
	PGTilesHand(CardMgr* Mgr):
		PGHand(Mgr) { };

	enum Constants { MAXTILES = 4 };

	int32 numCardsToDeal() { return 4; };

	virtual bool8 isSplitValid(int32 twocard[2]);
};


class PGPokerHand : public PGHand
{
	public:
		PGPokerHand(JokerCardMgr* Mgr);

		JokerCardMgr* jokMgr();

		enum Constants { MAXCARDS = 7 };

		int32 numCardsToDeal() { return 7; };

		bool8 isSplitValid(int32 twocard[2]);

		bool8 contains(int32 suit, int32 rank); 

		int32 findNonJokerIndex(int32 rank, int32 nOfRank);
		int32 findJokerIndex();

		int32 findNthHighestSuitIndex(int32 suit, int32 nOfSuit);
		int32 findNthHighestOffSuitIndex(int32 suit, int32 nOfOffSuit);
		int32 findOffSuitIndex(int32 suit, int32 nOfOffSuit);
};

inline JokerCardMgr*
PGPokerHand::jokMgr()
{
	return (JokerCardMgr*)mgr;
}

//
// --------------------------- PGPokerHandRanks ------------------------------
//

class PGHandRanks
{
public:
	virtual int32 compareLowRanks(PGHandRanks*) = 0;
	virtual int32 compareHighRanks(PGHandRanks*) = 0;
	virtual int32 getHighJokerAsRank() { return 0xff; /* no joker */ };
};



class PGTilesHandRanks : public PGHandRanks
{
	public:	
		enum WhichRanks { LowRankOnly, BothRanks };
		PGTilesHandRanks(PGTilesHand*, int32* twocard, WhichRanks = BothRanks);

		enum HandRanks
		{
			HR_ZERO,
			HR_ONE,
			HR_TWO,
			HR_THREE,
			HR_FOUR,
			HR_FIVE,
			HR_SIX,
			HR_SEVEN,
			HR_EIGHT,
			HR_NINE,
			HR_GONG,
			HR_WONG,
			HR_CHOP_NG,
			HR_CHOP_CHIT,
			HR_CHOP_BOT,
			HR_CHOP_GOW,
			HR_LOOK,
			HR_TILT,
			HR_PING,
			HR_FOO,
			HR_BON,
			HR_CHONG,
			HR_MOOY,
			HR_GOR,
			HR_YUN,
			HR_DAY,
			HR_TEEN,
			HR_GEE_JOON,
		};

		int32 getLowRank();					// low hand rank
		int32 getHighRank();				// high hand rank

		int32 getBestLowHandCard();
		int32 getBestHighHandCard();

		int32 compareLowRanks(PGHandRanks*);
		int32 compareHighRanks(PGHandRanks*);

	protected:
		int32 handPipCount(int32 twoCards[2]);
		HandRanks rankHand(int32 twoCards[2], int32* bestCard);
		HandRanks tileRank(int32 rank);
		int32 pipCount(int32 tilerank);

	protected:
		int32 loCards[2];
		int32 hiCards[2];
		HandRanks loRank;
		HandRanks hiRank;
		int32 bestLowHandCard;
		int32 bestHighHandCard;
};

inline int32
PGTilesHandRanks::getLowRank()
{
	return loRank;
}

inline int32
PGTilesHandRanks::getHighRank()
{
	return hiRank;
}

inline int32
PGTilesHandRanks::getBestLowHandCard()
{
	return bestLowHandCard;
}

inline int32
PGTilesHandRanks::getBestHighHandCard()
{
	return bestHighHandCard;
}

class PGPokerHandRanks : public PGHandRanks
{
	public:
		enum WhichRanks { LowRankOnly, BothRanks };
		PGPokerHandRanks(PGPokerHand*, int32* twocard, WhichRanks = BothRanks);

		enum HandRanks
		{
			HR_HIGHCARD,		
			HR_PAIR,		
			HR_2PAIR,		
			HR_3OFAKIND,
			HR_STRAIGHT,		
			HR_FLUSH,		
			HR_FULLHOUSE,	
			HR_4OFAKIND,
			HR_STRAIGHTFLUSH,	
			HR_ROYALFLUSH,
			HR_5OFAKIND
		};

		int32 getLowRank();					// low hand rank
		int32 getHighRank();				// high hand rank

		int32 getLowNthHighCard(int32);		// nth highest card of low hand
		int32 getHighNthHighCard(int32);	// nth highest card of high hand

		bool8 isHighAceLowStraight();

		int32 compareLowRanks(PGHandRanks*);
		int32 compareHighRanks(PGHandRanks*);

		int32 getTwoCard(int32 n);

		int32 getHighJokerAsRank();			// 0xff if no joker

	protected:
		int32 rankLowHand();
		int32 rankHighHand();
		int32 getHighNthHighCard(int32, int32);
		int32 compareAceLowStraights(PGPokerHandRanks*);

	protected:
		PGPokerHand* hand;
		int32 twoCard[2];
		int32 loRank;
		int32 hiRank;
		uint8 counts[14];
};

inline int32
PGPokerHandRanks::getLowRank()
{
	return loRank;
}

inline int32
PGPokerHandRanks::getHighRank()
{
	return hiRank;
}

inline int32
PGPokerHandRanks::getTwoCard(int32 n)
{
	ASSERT(n >= 0 && n < 2);

	return twoCard[n];
}

#endif
